Skip to main content

第 7 章:設定網路管理容器存取

外部存取容器

容器中可以執行一些網路應用,要讓外部也可以存取這些應用,可以通過 -P-p 參數來指定連接埠映射。

當使用 -P 參數時,Docker 會隨機映射一個 49000~49900 的連接埠到內部容器開放的網路連接埠。

使用 docker ps 可以看到,本地主機的 49155 被映射到了容器的 5000 連接埠。此時連結本機的 49155 連接埠即可連結容器內 web 應用提供的介面。

sudo docker run -d -P training/webapp python app.py

sudo docker ps -l

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

bc533791f3f5 training/webapp:latest python app.py 5 seconds ago Up 2 seconds 0.0.0.0:49155->5000/tcp nostalgic_morse

同樣的,可以透過 docker logs 命令來查看應用的訊息。

sudo docker logs -f nostalgic_morse

Running on http://0.0.0.0:5000/

10.0.2.2 - - [23/May/2014 20:16:31] "GET / HTTP/1.1" 200 -

10.0.2.2 - - [23/May/2014 20:16:31] "GET /favicon.ico HTTP/1.1" 404 -

p 則可以指定要映射的連接埠,並且在一個指定連接埠上只可以綁定一個容器。支援的格式有 ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort

映射所有遠端位址

使用 hostPort:containerPort 格式本地的 5000 連接埠映射到容器的 5000 連接埠,可以執行

sudo docker run -d -p 5000:5000 training/webapp python app.py

此時預設會綁定本地所有遠端上的所有位址。

映射到指定位址的指定連接埠

可以使用 ip:hostPort:containerPort 格式指定映射使用一個特定位址,比如 localhost 位址 127.0.0.1

sudo docker run -d -p 127.0.0.1:5000:5000 training/webapp python app.py

映射到指定位址的任意連接埠

使用 ip::containerPort 綁定 localhost 的任意連接埠到容器的 5000 連接埠,本地主機會自動分配一個連接埠。

sudo docker run -d -p 127.0.0.1::5000 training/webapp python app.py

還可以使用 udp 標記來指定 udp 連接埠

sudo docker run -d -p 127.0.0.1:5000:5000/udp training/webapp python app.py

查看映射連接埠配置

使用 docker port 來查看當前映射的連接埠配置,也可以查看到綁定的位址

docker port nostalgic_morse 5000

127.0.0.1:49155.

容器有自己的內部網路和 ip 位址(使用 docker inspect 可以獲取所有的變數,Docker 還可以有一個可變的網路設定。)

p 標記可以多次使用來綁定多個連接埠

sudo docker run -d -p 5000:5000  -p 3000:80 training/webapp python app.py

容器互連

容器的連線(linking)系統是除了連接埠映射外,另一種跟容器中應用互動的方式。

該系統會在來源端容器和接收端容器之間創建一個隧道,接收端容器可以看到來源端容器指定的資訊。

自訂容器命名

連線系統依據容器的名稱來執行。因此,首先需要自訂一個好記的容器命名。

雖然當創建容器的時候,系統會預設分配一個名字。自訂命名容器有2個好處:

自訂的命名,比較好記,比如一個web應用容器我們可以給它起名叫web

當要連線其他容器時候,可以作為一個有用的參考點,比如連線web容器到db容器

使用 --name 標記可以為容器自訂命名。

sudo docker run -d -P --name web training/webapp python app.py

使用 docker ps 來驗證設定的命名。

sudo docker ps -l

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

aed84ee21bde training/webapp:latest python app.py 12 hours ago Up 2 seconds 0.0.0.0:49154->5000/tcp web

也可以使用 docker inspect 來查看容器的名字

sudo docker inspect -f "{{ .Name }}" aed84ee21bde

/web

注意:容器的名稱是唯一的。如果已經命名了一個叫 web 的容器,當你要再次使用 web 這個名稱的時候,需要先用docker rm 來刪除之前建立的同名容器。

在執行 docker run 的時候如果新增 --rm 標記,則容器在終止後會立刻刪除。注意,--rm-d 參數不能同時使用。

容器互聯

使用 --link 參數可以讓容器之間安全的進行互動。

下面先建立一個新的資料庫容器。

sudo docker run -d --name db training/postgres

刪除之前建立的 web 容器

docker rm -f web

然後建立一個新的 web 容器,並將它連線到 db 容器

sudo docker run -d -P --name web --link db:db training/webapp python app.py

此時,db 容器和 web 容器建立互聯關系。

-link 參數的格式為 -link name:alias,其中 name 是要連線的容器名稱,alias 是這個連線的別名。

使用 docker ps 來查看容器的連線

docker ps

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

349169744e49 training/postgres:latest su postgres -c '/usr About a minute ago Up About a minute 5432/tcp db, web/db

aed84ee21bde training/webapp:latest python app.py 16 hours ago Up 2 minutes 0.0.0.0:49154->5000/tcp web

可以看到自訂命名的容器,db 和 web,db 容器的 names 列有 db 也有 web/db。這表示 web 容器連線到 db 容器,web 容器將被允許存取 db 容器的訊息。

Docker 在兩個互聯的容器之間創建了一個安全隧道,而且不用映射它們的連接埠到宿主主機上。在啟動 db 容器的時候並沒有使用 -p-P 標記,從而避免了暴露資料庫連接埠到外部網路上。

Docker 透過 2 種方式為容器公開連線訊息

  • 環境變數
  • 更新 /etc/hosts 檔案

使用 env 命令來查看 web 容器的環境變數

sudo docker run --rm --name web2 --link db:db training/webapp env

. . .
DB_NAME=/web2/db
DB_PORT=tcp://172.17.0.5:5432
DB_PORT_5000_TCP=tcp://172.17.0.5:5432
DB_PORT_5000_TCP_PROTO=tcp
DB_PORT_5000_TCP_PORT=5432
DB_PORT_5000_TCP_ADDR=172.17.0.5
. . .

其中 DB_ 開頭的環境變數是供 web 容器連線 db 容器使用,前綴採用大寫的連線別名。

除了環境變量,Docker 還新增 host 訊息到父容器的 /etc/hosts 的檔案。下面是父容器 web 的 hosts 檔案

sudo docker run -t -i --rm --link db:db training/webapp /bin/bash

root@aed84ee21bde:/opt/webapp# cat /etc/hosts
172.17.0.7 aed84ee21bde
. . .
172.17.0.5 db

這裡有 2 個 hosts,第一個是 web 容器,web 容器用 id 作為他的主機名,第二個是 db 容器的 ip 和主機名。 可以在 web 容器中安裝 ping 命令來測試跟db容器的連通。

root@aed84ee21bde:/opt/webapp# apt-get install -yqq inetutils-ping

root@aed84ee21bde:/opt/webapp# ping db

PING db (172.17.0.5): 48 data bytes
56 bytes from 172.17.0.5: icmp_seq=0 ttl=64 time=0.267 ms
56 bytes from 172.17.0.5: icmp_seq=1 ttl=64 time=0.250 ms
56 bytes from 172.17.0.5: icmp_seq=2 ttl=64 time=0.256 ms

用 ping 來測試db容器,它會解析成 172.17.0.5。 *注意:官方的 ubuntu 映像檔預設沒有安裝 ping,需要自行安裝。

使用者可以連線多個子容器到父容器,比如可以連線多個 web 到 db 容器上。

Docker的網路設定這一部份,在我們使用 Docker 時也常會被用到,因為我們啟動了一個 long time service 的 Docker container之後,如果希望能被其它的 Host 連到這個 service 就要設定網路這一個部份

host.png

例如 Host1 的實體主機需要連線到 Host2 的實體主機裡面的 Container1,如果在沒有設定網路情況下 Host1 連不到 Host2,因為 Container1 是被保護在 Host2 實體機器裡,如果要讓 Host1 連到 Host2 上,最簡單的方法是先讓 Host2 的 Port 對應到 Container1 的 Port,然後 Host1 會先連到 Host2 實體主機的 Port,然後就會對應到 Container1 的 Port,這樣 Host1 就可以連到 Host2 上的 Container1,這是最常使用的方法。

還有一種做法是直接讓 Container 橋接到實體主機網路卡的方式,讓 Container 能得到一組能讓其它 Host 連到的 IP,這樣就可以直接被其它的 Host 連接到,但這種做法會讓 Container 暴露在外,這時侯有必要考慮到安全性上的問題。

另外還有一種需求是 Container1 需要直接連到 Container2

container.png

就是在相同的 Host 的情況下,如果要讓 Container1 的 Web Server,能連到 Container2 的 db server,可以使用 docker run 的 --link 參數。

連線範例

  1. 在啟動 Container 的時侯設定實體主機 Host 的 Port 對應到 Container 裡面的 Port
  2. 實作讓 Container2 可以直接的連到 Container1

Example 1:主機隨機產生 port

測試在使用 docker run 指令時參數指定為大寫的 -P,這樣實體主機會隨機的產生一個 Port 對應到 Container 裡面的 Port,但在這之前需要修改一下 Dockerfile,實作的步驟

1. Dockerfile 

FROM java
MAINTAINER jack
RUN apt-get update
RUN apt-get install -y wget

RUN cd /

RUN wget http://apache.stu.edu.tw/tomcat/tomcat-7/v7.0.82/bin/apache-tomcat-7.0.82.tar.gz

RUN tar zxvf apache-tomcat-7.0.82.tar.gz

CMD ["/apache-tomcat-7.0.82/bin/catalina.sh", "run"]

EXPOSE 8080

EXPOSE 就是宣告有 8080 Port,在執行 Docker Container時需要把它開放出去

Build Docker Image 指令

docker build -t tomcatporttest .

Build 完 Image 之後,使用以下的指令啟動 Docker Container 指令

docker run -d -P tomcatporttest

我們可以使用 docker ps 指令或是 docker port 指令得到對應實體主機隨機產生出來的 Port

docker ps -a
$ docker port 2fec91391edb

2fec91391edb 為 ContainerID

5上個步驟可以得出實體主機對應的 Port 為 32770,因此我們可以用實體主機的IP 和 32770 Port,連到 Tomcat 的 Web 畫面

Example 2:指定主機 port

使用 Example1 的 Docker Image,在執行 docker run 時,指定小寫的-p參數

$ docker run -d -p 8080:8080 tomcatporttest

左邊的 8080 Port 代表指定實體主機的 Port,對應到右邊 Container 裡面的 8080 Port

Example 3:連結兩個 container

啟動 container1 的 Docker Container,然後再啟動 container2 並且 link 到 container1,最後測試看看直接在 container2 上是否 ping 得到 container1

啟動 container1

docker run -it --name container1 centos /bin/bash

開啟另外一個視窗,啟動 container2

docker run -it --name container2 --link container1 centos /bin/bash

測試 container2 是否 ping 得到 container1

執行 docker run 時要如何指定對應的 port 和如何在 Dockerfile 裡面寫 Expose 指令,以及也說明如何使用在同一台實體主機上的 Container 之間要如何的連結起來

在執行 docker run 指令時,有一個參數是 --net,它可以設定在執行 Docker Container 是要使用哪一種的網路模式,目前所知道的網路模式有 none、container、host、bridge、overlay… 等等的模式,它們是透過使用 Linux 的 libnetwork 所建立出來的網路模式,以下分別說明這些網路模式

  • none: 在執行 container 時,網路功能是關閉的,所以無法與此 container 連線
  • container: 使用相同的 Network Namespace,所以 container1 的 IP 是 172.17.0.2 那 container2 的 IP 也會是 172.17.0.2
  • host: container 的網路設定和實體主機使用相同的網路設定,所以 container 裡面也就可以修改實體機器的網路設定,因此使用此模式需要考慮網路安全性上的問題
  • bridge: Docker 預設就是使用此網路模式,這種網路模式就像是 NAT 的網路模式,例如實體主機的 IP 是 192.168.1.10 它會對應到 Container 裡面的 172.17.0.2,在啟動 Docker 的 service 時會有一個 docker0 的網路卡就是在做此網路的橋接。
  • overlay: container 之間可以在不同的實體機器上做連線,例如 Host1 有一個 container1,然後 Host2 有一個 container2,container1 就可以使用 overlay 的網路模式和 container2 做網路的連線。

overlay.png

上圖主要是說明 Host1 實體主機裡面有 Container1,然後 Host2 實體主機裡面有 Container2,可以透過 Docker Overlay 的 Network 的模式將 Container1 和 Container2 連接起來做溝通。另外 Consol 是一個存放連線資訊的資料庫,在使用 overlay 時必需要在 Docker 設定,這樣才能存放 overlay 網路模式的連線資訊

另外在使用 overlay 網路模式時,要先確認 Linux Kernel 的版本

Example 4:None 網路模型

在使用 docker run 指令時,指定 --net=none 的網路模式,確認執行結果

docker run -it --net=none joffotron/docker-net-tools

ifconfig

進入 container 入後輸入 ifconfig 指令

從上圖可以看到除了 127.0.0.1 之外沒有其它的 IP 位址,另外 ping google 看看

結果確定了 none 的模式關閉了網路連線

Example 5:container 網路模型

測試開啟了 container1 之後,再開啟 container2 並且設定 --net=container:container1的參數,測試 container1和container2的網路資訊是否相同

  • 啟動 container1 指令
docker run --name container1 -it joffotron/docker-net-tools
  1. 新開一個視窗,啟動 container2 指令
docker run --name container2 --net=container:container1 -it joffotron/docker-net-tools

ifconfig
  1. 輸入ifconfig指令,查看 container1 和 container2 的網路資訊內容是否相同

上圖可以確認 container1 和 container2 的網路資訊是相同的

Example 6:host 網路模型

在執行 docker run 指令時,參數指定為 --net=host,測試 host 模式。

執行的指令

docker run --net=host -it joffotron/docker-net-tools

ifconfig

可以看到 Container 的網路資訊和實體主機的網路資訊是相同的結果

Example 7:bridge 網路模型

在執行 docker run 指令時,參數指定為 --net=bridge,測試 bridge模式。

執行的指令

docker run --net=bridge -it joffotron/docker-net-tools

ifconfig

可以看到使用 Bridge模式,會建立了 172.17.0.2 的 IP,它會橋接到實體主機的docker0的虛擬網路卡。

今天已經介紹了 Docker 的網路模式和實作了 none、container、host、bridge的網路模式,明天會繼續實作 overlay 的網路模式。

Example 8:overlay 網路模型

切換到 root 使用者,修改 Host1 的 Docker 設定

vi /etc/docker/daemon.json
{
"live-restore": true,
"group": "dockerroot",
"hosts": [
"unix:///var/run/docker.sock",
"tcp://10.1.0.221:2375"
],
"cluster-store": "consul://10.1.0.221:8500",
"cluster-advertise": "enp8s0:2375"
}

主要的設定就是 cluster-store 和 cluster-advertise,分別是要連接到 consul 的 service,取得 overlay 網路模式的連線資訊,以及設定可以讓不同實體主機連線的網路卡。

重新啟動 Host1 的 Docker Service

systemctl restart docker

在 Host1 使用啟動 docker container 的方式啟動 consul service

docker run -d -p 8500:8500 progrium/consul -server -bootstrap

在 Host1,使用 docker network 指令建立一個 overlay1 的網路

docker network create -d overlay overlay1

docker network ls

在 Host1 啟動一個連接到 overlay1 的 container

docker run -it --name=container1 --net=overlay1 busybox

開一個新視窗,到 Host2 修改 Docker 的設定

vi /etc/docker/daemon.json

ifconfig
{
"live-restore": true,
"group": "dockerroot",
"hosts": [
"unix:///var/run/docker.sock",
"tcp://10.1.0.223:2375"
],
"cluster-store": "consul://10.1.0.221:8500",
"cluster-advertise": "enp8s0:2375"
}

在 Host2 上重新啟動 Docker service 指令

systemctl restart docker

ifconfig

在 Host2 上,使用 docker network ls 指令確認是否有連到 consol service

在 Host2 上啟動一個 container,並且連到 overlay1 的網路

docker run -it --name=container2 --net=overlay1 busybox

ifconfig

測試在 container1 ping 到 container2 的 IP